package cn.itcast.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfiguration {

    //配置自定义的Realm
    @Bean
    public CustomRealm getRealm(){
        return new CustomRealm();
    }

    //配置安全管理器
    @Bean
    public SecurityManager securityManager(CustomRealm customRealm){
        //使用默认的安全管理器
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(customRealm);
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(redisCacheManager());
        //将自定义的realm交给安全管理器统一调度管理
        securityManager.setRealm(customRealm);
        return securityManager;
    }

    //Filter工厂，设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        //1.创建shiro过滤器工厂
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //2.设置安全管理器
        factoryBean.setSecurityManager(securityManager);
        //3.通用配置（配置登录页面，登录成功页面，验证未成功页面）
        factoryBean.setLoginUrl("/autherror?code=1");       //设置登录页面
        factoryBean.setUnauthorizedUrl("/autherror?code=2");//授权失败跳转页面

        //4.配置过滤器集合
        /**
         * key ：访问连接
         *     支持通配符的形式
         * value：过滤器类型
         *     shiro常用过滤器
         *         anno   ：匿名访问（表明此链接所有人可以访问）
         *         authc   ：认证后访问（表明此链接需登录认证成功之后可以访问）
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
        // 配置不会被拦截的链接 顺序判断
//        filterMap.put("/user/home", "anon");
        // TODO 具有指定访问权限  使用filterMap.put 方法，setUnauthorizedUrl 会有效；
        // TODO 然而在方法上使用@RequiresPermissions(value = "user-find"), setUnauthorizedUrl 无效， 可以配置全局AuthorizationException异常抓取，
        //  然后在跳转到指定页面
        filterMap.put("/user/find", "perms[user-find]");
        //认证之后访问（登录之后可以访问）
        filterMap.put("/user/**", "authc");
//        //具有指定角色可以访问
//        filterMap.put("/user/**", "roles[系统管理员]");

        factoryBean.setFilterChainDefinitionMap(filterMap);
        return factoryBean;
    }

    //配置shiro注解支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        sourceAdvisor.setSecurityManager(securityManager);
        return sourceAdvisor;
    }


    @Value("${spring.redis.host}")
    String host;
    @Value("${spring.redis.port}")
    int port;

    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        return redisManager;
    }

    //配置Shiro的缓存管理器
    //使用redis实现
    public RedisCacheManager redisCacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    public RedisSessionDAO redisSessionDAO(){
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * 3.会话管理器
     */
    public DefaultWebSessionManager sessionManager(){
        CustomSessionManager sessionManager = new CustomSessionManager();
        //定义session 超时时间？？？？
        //sessionManager.setGlobalSessionTimeout(1800000L);
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

}
